#include <math.h>
#include <stdlib.h>

typedef struct {
	unsigned char r, g, b;
} color;

typedef struct {
	color average;
	color std_dev;
} color_stats;

float square(float dif) {
	return dif * dif;
}

float MIN(float a, float b, float c) {
	if (a < b) {
		if (a < c) return a;
		return c;
	}
	else {
		if (b < c) return b;
		return c;
	}
}

float MAX(float a, float b, float c) {
	if (a > b) {
		if (a > c) return a;
		return c;
	}
	else {
		if (b > c) return b;
		return c;
	}
}

void RGBtoHSV(float r, float g, float b, float *h, float *s, float *v)
{
	r /= 255.f;
	g /= 255.f;
	b /= 255.f;

	float min, max, delta;
	min = MIN(r, g, b);
	max = MAX(r, g, b);
	*v = max;                                //        v
	delta = max - min;
	if (max != 0)
		*s = delta / max;              // s 
	else {
		// r = g = b = 0               // s = 0, v is undefined 
		*s = 0;
		*h = -1;
		return;
	}
	if (r == max)
		*h = (g - b) / delta;                // between yellow & 
	else if (g == max)
		*h = 2 + (b - r) / delta;    // between cyan & yellow 
	else
		*h = 4 + (r - g) / delta;    // between magenta & cyan
	*h *= 60;                                //        degrees        
	if (*h < 0)
		*h += 360;
}

void HSVtoRGB(float *r, float *g, float *b, float h, float s, float v)
{
	int i;
	float f, p, q, t;

	if (s == 0) {
		// achromatic (grey)
		*r = *g = *b = v;
		return;
	}

	h /= 60;			// sector 0 to 5
	i = floor(h);
	f = h - i;			// factorial part of h
	p = v * (1 - s);
	q = v * (1 - s * f);
	t = v * (1 - s * (1 - f));

	switch (i) {
	case 0:
		*r = v;
		*g = t;
		*b = p;
		break;
	case 1:
		*r = q;
		*g = v;
		*b = p;
		break;
	case 2:
		*r = p;
		*g = v;
		*b = t;
		break;
	case 3:
		*r = p;
		*g = q;
		*b = v;
		break;
	case 4:
		*r = t;
		*g = p;
		*b = v;
		break;
	default:		// case 5:
		*r = v;
		*g = p;
		*b = q;
		break;
	}

}

color_stats get_stats(unsigned char *data, int data_count) {
	float h, s, v;

	int points = 0;

	float ar = 0, ag = 0, ab = 0;
	for (int i = 0; i < data_count; i += 4) {
		ar += data[i];
		ag += data[i + 1];
		ab += data[i + 2];
		++points;
	}
	ar /= points;
	ag /= points;
	ab /= points;

	float sr = 0, sg = 0, sb = 0;
	for (int i = 0; i < data_count; i += 4) {
		sr += square(data[i] - ar);
		sg += square(data[i + 1] - ag);
		sb += square(data[i + 2] - ab);
	}
	sr /= (points - 1);
	sg /= (points - 1);
	sb /= (points - 1);

	sr = sqrt(sr);
	sg = sqrt(sg);
	sb = sqrt(sb);

	color average = { ar, ag, ab };
	color std_dev = { sr, sg, sb };
	color_stats stats = { average, std_dev };
	return stats;
}

void process(unsigned char *data, int size, char *str_fallback, float *lr, float *lg, float *lb, float *rr, float *rg, float *rb, float *br, float *bg, float *bb, float *fr, float *fg, float *fb) {
	float h, s, v;
	
	int total = 0;
	int index = 0;
	while (str_fallback[index]) {
		total += *(unsigned char*)(str_fallback + index);
		++index;
	}

	srand(total);

	if (data) {
		color_stats stats = get_stats(data, size);

		unsigned char *narrow_data = malloc(sizeof(unsigned char) * size);

		int ndlength = 0;

		float std_dist = sqrt(square(stats.std_dev.r) + square(stats.std_dev.g) + square(stats.std_dev.b));
		for (int i = 0; i < size; i += 4) {
			float i_dist = sqrt(square(data[i] - stats.average.r) + square(data[i + 1] - stats.average.g) + square(data[i + 2] - stats.average.b));
			if (i_dist > std_dist * 1.5f) {
				narrow_data[ndlength++] = data[i];
				narrow_data[ndlength++] = data[i + 1];
				narrow_data[ndlength++] = data[i + 2];
				narrow_data[ndlength++] = data[i + 3];
			}
		}

		color_stats ndstat = get_stats(narrow_data, ndlength);

		RGBtoHSV(ndstat.average.r, ndstat.average.g, ndstat.average.b, &h, &s, &v);

		if (s < 0.03) {
			s = 0.8;
			h = (rand() / (float)RAND_MAX) * 360.f;
		}


		free(narrow_data);
	}
	else {
		s = (rand() / (float)RAND_MAX);
		h = (rand() / (float)RAND_MAX) * 360.f;

		if (s < 0.03) {
			s = 0.8;
		}
	}


	
	HSVtoRGB(lr, lg, lb, h, s, 1.f);
	h += 20;
	if (h > 360) h -= 360;
	if (h < 0) h += 360;
	HSVtoRGB(rr, rg, rb, h, s, 1.f);
	h += 40;
	if (h > 360) h -= 360;
	if (h < 0) h += 360;
	HSVtoRGB(fr, fg, fb, h, s, 1.f);
	HSVtoRGB(br, bg, bb, h, s, 0.3f);
}